`ifndef MY_DRIVER_SVH
`define MY_DRIVER_SVH

typedef class my_transaction;
`ifdef USER_UVM_DRIVER_PARAMETER_PASS_TEST
class my_driver extends uvm_driver #(my_transaction);
`else
class my_driver extends uvm_driver;
`endif
    `uvm_component_utils(my_driver)
    virtual my_if vif;
    function new(string name = "my_driver", uvm_component parent = null);
        super.new(name, parent);
        `uvm_info("my_driver", "new is called", UVM_LOW)
    endfunction
    virtual function void build_phase(uvm_phase phase);
        super.build_phase(phase);
        `uvm_info("my_driver", "build_phase is called", UVM_LOW)
        if(!uvm_config_db#(virtual my_if)::get(this, "", "vif", vif))
            `uvm_fatal("my_driver", "virtual ifterface must be set")
    endfunction
    extern task main_phase(uvm_phase phase);
    extern task drive_one_pkt(my_transaction tr);
endclass

task my_driver::main_phase(uvm_phase phase);
`ifdef USER_UVM_DRIVER_PARAMETER_PASS_TEST
`else
    my_transaction tr;
`endif
    // Only where a sequence appears will <raise_objection> be called. 
    //phase.raise_objection(this);
    `uvm_info("my_driver", "main_phase is called", UVM_LOW)
    vif.data   <= 8'b0;
    vif.valid <= 1'b0;
    while(!vif.rstn)begin
        @(posedge vif.clk);
    end 
//    for(int i = 0; i < 256; i++)begin
//        @(posedge vif.clk);
//        vif.data   <= $urandom_range(0, 256);
//        vif.valid <= 1'b1;
//        `uvm_info("my_driver", "data is drived", UVM_LOW)
//        @(posedge vif.clk);
//        vif.valid <= 1'b0;
//    end
//    @(posedge vif.clk);
//    vif.valid <= 1'b0;
    // The following code can replace the above code
`ifdef USER_UVM_DRIVER_NO_GENERATE_TRANSACTION_TEST
    while(1)begin
`ifdef USER_UVM_GET_NEXT_ITEM_TEST
        // <get_next_item> is blocking, it will block simution
        seq_item_port.get_next_item(req);
        drive_one_pkt(req);
        seq_item_port.item_done();
`else
        // <try_next_item> is non-blocking
        seq_item_port.try_next_item(req);
        if(req == null)begin
            @(posedge vif.clk);
        end else begin
            drive_one_pkt(req);
            seq_item_port.item_done();
            `uvm_info(this.get_type_name(), "dirved pkt info", UVM_LOW)
            req.print();
        end
`endif
    end
`else
`ifdef USER_UVM_DRIVER_PARAMETER_PASS_TEST
    // req is the member variable in uvm_driver and 
    // its type is the passed type like "my_transaction" in line 6
    for(int i = 0; i < 2; i++)begin
        req = new("req");
        assert(req.randomize() with {req.pload.size() == 200;});
        drive_one_pkt(req);
        `uvm_info("my_driver", "pkt information", UVM_LOW)
        req.print();
    end
`else
    for(int i = 0; i < 2; i++)begin
        tr = new("tr");
        assert(tr.randomize() with {tr.pload.size() == 200;});
        drive_one_pkt(tr);
        `uvm_info("my_driver", "pkt information", UVM_LOW)
        tr.print();
    end
`endif
`endif
    repeat(5) begin
        @(posedge vif.clk);
    end
    //phase.drop_objection(this);
endtask

task my_driver::drive_one_pkt(my_transaction tr);
    bit [47:0] tmp_data;
`ifdef USER_UVM_FIELD_AUTOMATION_PACK_TEST
    bit [ 7:0] data_q[];
    int data_size; 
    int pack_data_size; 
`else
    bit [ 7:0] data_q[$];
`endif

`ifdef USER_UVM_FIELD_AUTOMATION_PACK_TEST
    pack_data_size = tr.pack_bytes(data_q);
    //`uvm_info(this.get_type_name(), $sformatf("pack_bytes number is %0d", pack_data_size), UVM_LOW);
    data_size = pack_data_size / 8;
    `uvm_info(this.get_type_name(), $sformatf("packed byte number is %0d", data_size), UVM_LOW);
`else
    int pload_size = tr.pload.size();
    // push dmac to data_q
    tmp_data = tr.dmac;
    for(int i = 0; i < 6; i++)begin
        data_q.push_back(tmp_data[7:0]);
        tmp_data = tmp_data >> 8;
    end

    //push smac to data_q
    tmp_data = tr.smac;
    for(int i = 0; i < 6; i++)begin
        data_q.push_back(tmp_data[7:0]);
        tmp_data = tmp_data >> 8;
    end
    // push ether_type to data_q
    tmp_data[15:0] =  tr.ether_type;
    for(int i = 0; i < 2; i++)begin
        data_q.push_back(tmp_data[7:0]);
        tmp_data = tmp_data >> 8;
    end
    // push pload to data_q
    for(int i = 0; i < pload_size; i++)begin
        data_q.push_back(tr.pload[i]);
    end
    tmp_data[31:0] = tr.crc;
    for(int i = 0; i < 4; i++)begin
        data_q.push_back(tmp_data[7:0]);
        tmp_data = tmp_data >> 8;
    end
`endif
    `uvm_info("my_driver", "begin to dirve one pkt", UVM_LOW)
    repeat(3)begin
        @(posedge vif.clk);
    end
`ifdef USER_UVM_FIELD_AUTOMATION_PACK_TEST
    // <pack_bytes> must be dynamic array
    for(int i = 0; i < data_size; i++)begin
        @(posedge vif.clk);
        vif.valid <= 1'b1;
        vif.data  <= data_q[i];
    end
`else
    while(data_q.size() > 0) begin
        @(posedge vif.clk);
        vif.valid <= 1'b1;
        vif.data  <= data_q.pop_front();
    end
`endif
    @(posedge vif.clk);
    vif.valid <= 1'b0;
    `uvm_info("my_driver", "end to dirve one pkt", UVM_LOW)
endtask
`endif
